package com.oig.sys.security.config;


import cn.hutool.core.util.ArrayUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;

/**
 * 请求 resource-server 过程
 * BearerTokenAuthenticationFilter: 解析 Authorization: Bearer {token} 中的token
 *      交给 OpaqueTokenAuthenticationProvider
 *          OpaqueTokenAuthenticationProvider 委托 OpaqueTokenIntrospector 的 introspect 去校验 token
 *          OpaqueTokenIntrospector
 *              NimbusOpaqueTokenIntrospector: OpaqueTokenIntrospector的实现
 *              添加 BasicAuthenticationInterceptor 拦截器
 *                  在认证之前，需要对该resource-server 的 client_id 和client_secret 进行认证
 *                  在Header 上添加 Authentication: Basic {Base64.encode(client_id:client_secret)}
 *                  在authentication-server 会被 OAuth2ClientAuthenticationFilter 拦截
 *          RestTemplate
 *              通过 RestTemplate 去请求 authentication-server 的 /oauth2/introspect
 *              在authentication-server 会被 OAuth2TokenIntrospectionEndpointFilter 拦截
 * authentication-server 认证过程:
 * OAuth2ClientAuthenticationFilter
 *      OAuth2ClientAuthenticationFilter 的 ClientSecretBasicAuthenticationConverter 会检索出 Authentication 中 Basic 的token
 *      委托 ClientSecretAuthenticationProvider 对 client_id 和 client_secret 进行认证
 *      认证成功，交给下一个Filter
 * OAuth2TokenIntrospectionEndpointFilter
 *      拦截·/oauth2/introspect 请求
 *      委托 OAuth2TokenIntrospectionAuthenticationProvider 认证
 *          OAuth2TokenIntrospectionAuthenticationProvider
 *          通过 OAuth2AuthorizationService 根据 token 到数据库查询 是否有 认证过的 OAuth2Authorization
 *          返回 OAuth2TokenIntrospectionAuthenticationToken
 */
@Slf4j
@RequiredArgsConstructor
@AutoConfigureAfter(SecurityAutoConfig.class)
public class SecurityResourceConfig  {

    private final PermitUrlProperties permitUrlProperties;
    private final ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint ;
    private final ResourceAccessDeniedHandler resourceAccessDeniedHandler;

    @Value("${spring.security.oauth2.resourceserver.opaquetoken.introspection-uri}")
    private String introspectionUri;
    @Value("${spring.security.oauth2.resourceserver.opaquetoken.client-id}")
    private String clientId;
    @Value("${spring.security.oauth2.resourceserver.opaquetoken.client-secret}")
    private String clientSecret;

    /**
     * 这个才是会忽略身份认证，就算带token也不会去检查token
     */
    @Bean
    public WebSecurityCustomizer ignoreRequest() {
        return (web) -> web.ignoring().antMatchers(ArrayUtil.toArray(permitUrlProperties.getUrls(), String.class));
    }


    /**
     * authorize.antMatchers(ArrayUtil.toArray(permitUrlProperties.getUrls(), String.class)).permitAll()
     * 是忽略授权检查
     * 不是身份认证，所以带了token时，还是会去做身份认证
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain securityFilterChain(HttpSecurity http)  throws Exception {
        log.info("security resource config is load...");
        http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()).csrf().disable().cors().and()
//                .exceptionHandling(exception->exception
//                        .accessDeniedHandler(resourceAccessDeniedHandler)       //
//                        .authenticationEntryPoint(resourceAuthExceptionEntryPoint))
                .oauth2ResourceServer(token->token
                        .accessDeniedHandler(resourceAccessDeniedHandler)   ////没有效果,在ControllerAdvice 里就被处理了，这里捕获不到了
                        .authenticationEntryPoint(resourceAuthExceptionEntryPoint)
//                        .jwt(jwtConfigurer->jwtConfigurer.jwtAuthenticationConverter(new CustomizeJwtAuthenticationConverter())));
                        .opaqueToken(opaqueConfigurer -> opaqueConfigurer
                                .introspector(new CustomizeOpaqueTokenIntrospector(introspectionUri,clientId, clientSecret))));
        return http.build();
    }

}
